package com.airbat.prize.utils;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * Created by djw on 2017/2/20.
 */
public class FileUtil
{
    private  static Logger logger = LoggerFactory.getLogger(FileUtil.class);

    /**
     * 检查目录，自动层级向下新建目录;目录存在不会报错
     * @param path 如：abc/def/ghk, D:/abc/def
     */
    public static void checkDirectory(String path)
    {
        if (StringUtils.isEmpty(path))
            return;
        Path newdir = FileSystems.getDefault().getPath(path);
        //Path newdir = Paths.get(path);
        try
        {
            if (!Files.exists(newdir))
                Files.createDirectories(newdir);
        }
        catch (IOException e)
        {
            logger.error("异常",e);
        }
    }

    /**
     * 检查文件是否可以访问
     */
    public static boolean checkFileAccess(File file)
    {
        return file.exists()&&file.renameTo(file);
    }
    /**
     * 读取文件夹dir下的所有文件并且保存到list数值中，如果extNames为空的话，则读取所有的子文件,
     * 如果不为空的话,则只读取文件类型在extNames中的文件。
     * 如果lastTime非空，则只返回更新时间在这个时间之前的文件
     * @param dir
     * @param extNames:文件后缀
     * @return
     */
    public static List<String> listFiles(String dir, List<String> extNames, Date lastUpdateTime, List<String> prename)
    {
        return listFiles(dir, extNames, lastUpdateTime, prename, -1);
    }
    /**
     * 获取文件扩展名
     * @param filename
     * @return
     */
    public static String getNamefromFilename(String filename)
    {
        int index = filename.lastIndexOf(".");
        String name = filename;
        if (index != -1)
        {
            name = filename.substring(0, index);
        }
        return name;
    }
    /**
     * 获取文件扩展名
     * @param filename
     * @return
     */
    public static String getExtfromFilename(String filename)
    {
        int index = filename.lastIndexOf(".");
        String extname = null;
        if (index != -1)
        {
            extname = filename.substring(index + 1);
        }
        return extname;
    }
    /**
     * 读取文件夹dir下的所有文件并且保存到list数值中，如果extNames为空的话，则读取所有的子文件,
     * 如果不为空的话,则只读取文件类型在extNames中的文件。
     * 如果lastTime非空，则只返回更新时间在这个时间之前的文件
     * @param dir
     * @param extNames:文件后缀
     * @return
     */
    public static List<String> listFiles(String dir, List<String> extNames, Date lastUpdateTime, List<String> prename, int max)
    {
        List<String> filesList = new ArrayList<String>();
        if (dir == null)
        {
            return filesList;
        }

        File file = new File(dir);
        if (file.exists() && file.isDirectory())
        {
            File[] files = file.listFiles();
            for (File f : files)
            {

                if (f.exists() && f.isFile())
                {
                    String fileName = f.getName();
                    String extName = getExtfromFilename(fileName);
                    if (extName != null)
                        extName = extName.toLowerCase();
                    String name = getNamefromFilename(fileName);
                    boolean match = true;
                    if (extNames != null && extNames.size() > 0)
                    {
                        if (!extNames.contains(extName))
                            match = false;
                    }
                    if (prename != null && prename.size() > 0)
                    {
                        boolean ok = false;
                        for(String pre : prename)
                        {
                            if(name.startsWith(pre))
                            {
                                ok = true;
                                break;
                            }
                        }
                        if (!ok)
                            match = false;
                    }
                    if (lastUpdateTime != null)
                    {
                        if (f.lastModified() >= lastUpdateTime.getTime())
                            match = false;
                    }
                    if (match)
                        filesList.add(fileName);
                }
                if(max > 0 && filesList.size() >=max)
                {
                    logger.info("总文件数量有" + files.length + "个，取" + max + "个文件");
                    break;
                }
            }
        }
        else
        {
            return filesList;
        }

        return filesList;
    }

    /**
     * 移动文件
     * @param sourcePath 原路径
     * @param destPath 目标路径
     * @return
     */
    public static synchronized boolean moveFile(String sourcePath, String destPath)
    {
        boolean result = false;
        try
        {
            if (StringUtils.isBlank(sourcePath))
                return true;
            File sourceFile = new File(sourcePath);
            if (!sourceFile.exists())
                return true;
            File dest = new File(destPath);
            if ((dest.exists()))
                dest.delete();
            result = sourceFile.renameTo(dest);
        }
        catch (Exception e)
        {
            logger.error("异常",e);
        }
        return result;
    }

    /**
     * 判断文件是否存在
     * @param path
     * @return
     */
    public static boolean isFileExsit(String path)
    {
        File file = new File(path);
        return file.exists();
    }
    /**
     * 通过MD5文件文件名创建目录
     * @param path
     * @return
     */
    public static void createDirByMD5(String path,String md5)
    {
        File file = new File(path);
        String md5Dir = md5.substring(0,2);
        try
        {
            File fileDir = new File(file.getCanonicalPath()+File.separatorChar+md5Dir);
            if(!fileDir.exists())
            {
                fileDir.mkdirs();
            }
        }
        catch (Exception e)
        {

        }
    }
    /**
     * 读取文件内容
     * @param fileName
     * @return
     */
    public static byte[] readFileContent(String fileName)
    {
        RandomAccessFile file = null;
        byte[] f = null;
        try
        {
            file = new RandomAccessFile(fileName, "r");
            f = new byte[(int)file.length()];
            file.read(f);
        }
        catch (Exception e)
        {
            logger.error("异常",e);
        }
        finally
        {
            try
            {
                if (file != null)
                    file.close();
            }
            catch (IOException e)
            {
                e.printStackTrace();
            }
        }

        return f;
    }
    //读取文件内容
    public static String readFileContent(File file)
    {
        StringBuffer fileContent = new StringBuffer();
        BufferedReader reader = null;
        try
        {
            reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8")); // 指定读取文件的编码格式，要和写入的格式一致，以免出现中文乱码,
            String str = null;
            while ((str = reader.readLine()) != null)
            {
                fileContent.append(str);
            }
        }
        catch (Exception e)
        {
            logger.error("异常",e);
        }
        finally
        {
            try
            {
                reader.close();
            }
            catch (IOException e)
            {
                e.printStackTrace();
            }
        }
        return fileContent.toString();
    }
    //-----------------------------------------------------------------------
    /**
     * 强力删除文件,如果是文件夹，则删除所有的文件和子文件夹
     * <p>
     * The difference between File.delete() and this method are:
     * <ul>
     * <li>A directory to be deleted does not have to be empty.</li>
     * <li>You get exceptions when a file or directory cannot be deleted.
     *      (java.io.File methods returns a boolean)</li>
     * </ul>
     *
     * @param file  file or directory to delete, must not be {@code null}
     * @throws NullPointerException if the directory is {@code null}
     * @throws FileNotFoundException if the file was not found
     * @throws IOException in cases deletion is unsuccessful
     */
    public static void forceDelete(File file)
        throws IOException
    {
        if (file.isDirectory())
        {
            deleteDirectory(file);
        }
        else
        {
            boolean filePresent = file.exists();
            if (!file.delete())
            {
                if (!filePresent)
                {
                    throw new FileNotFoundException("File does not exist: " + file);
                }
                String message = "Unable to delete file: " + file;
                throw new IOException(message);
            }
        }
    }
    /**
     * 删除文件夹
     *
     * @param directory  文件夹
     * @throws IOException
     **/

    public static void deleteDirectory(File directory)
        throws IOException
    {
        if (!directory.exists())
        {
            return;
        }

        cleanDirectory(directory);

        if (!directory.delete())
        {
            String message = "Unable to delete directory " + directory + ".";
            throw new IOException(message);
        }
    }
    /**
     * Cleans a directory without deleting it.
     *
     * @param directory directory to clean
     * @throws IOException in cases cleaning is unsuccessful
     */
    public static void cleanDirectory(File directory)
        throws IOException
    {
        if (!directory.exists())
        {
            String message = directory + " does not exist";
            throw new IllegalArgumentException(message);
        }

        if (!directory.isDirectory())
        {
            String message = directory + " is not a directory";
            throw new IllegalArgumentException(message);
        }

        File[] files = directory.listFiles();
        if (files == null)
        { // null if security restricted
            throw new IOException("Failed to list contents of " + directory);
        }

        IOException exception = null;
        for (File file : files)
        {
            try
            {
                forceDelete(file);
            }
            catch (IOException ioe)
            {
                exception = ioe;
            }
        }

        if (null != exception)
        {
            throw exception;
        }
    }
    /**
     * 采用递归遍历文件 ，如果遍历到的文件是文件，并且名字以fileName结尾的话，则 把当前文件加入到 files列表中
     * @param files
     * @param dir
     * @param fileName
     */
    public static void listFile(List<File> files, File dir, String fileName)
    {
        if (dir == null || !dir.exists())
            return;
        if (dir.isDirectory())
        {
            File listFiles[] = dir.listFiles();
            for (File file : listFiles)
            {
                listFile(files, file, fileName);
            }
        }
        else if (dir.getName().endsWith(fileName))
        {
            files.add(dir);
        }
    }
    public static void saveFile(String filepath, byte[] content)
    {
        if (content == null || filepath == null)
        {
            logger.error("content为空或filepath为空.");
            return;
        }
        RandomAccessFile file = null;
        try
        {
            if (!isFileExsit(filepath))
            {
                file = new RandomAccessFile(filepath, "rw");
                file.setLength(content.length);
                file.close();
            }
            file = new RandomAccessFile(filepath, "rw");
            file.setLength(content.length);
            file.write(content);
        }
        catch (Exception e)
        {
            logger.error("异常",e);
        }
        finally
        {
            try
            {
                file.close();
            }
            catch (IOException e)
            {
                e.printStackTrace();
            }
        }
    }
    /************  递归删除文件及文件夹 start   *******************/
    public static boolean deleteFile(File file)
    {
        // 判断目录或文件是否存在
        if (file.exists())
        {
            // 判断是否为文件
            if (file.isFile())
            { // 为文件时调用删除文件方法
                return deleteFile(file.getAbsolutePath());
            }
            else
            { // 为目录时调用删除目录方法
                return deleteDirectory(file.getAbsolutePath());
            }
        }
        return true;
    }
    public static boolean deleteFile(String sPath)
    {
        boolean flag = false;
        File file = new File(sPath);
        // 路径为文件且不为空则进行删除
        if (file.isFile() && file.exists())
        {
            file.delete();
            flag = true;
        }
        return flag;
    }
    /**
     * 删除目录（文件夹）以及目录下的文件
     * @param   sPath 被删除目录的文件路径
     * @return  目录删除成功返回true，否则返回false
     */
    private static boolean deleteDirectory(String sPath)
    {

        //如果sPath不以文件分隔符结尾，自动添加文件分隔符
        if (!sPath.endsWith(File.separator))
        {
            sPath = sPath + File.separator;
        }
        File dirFile = new File(sPath);
        //如果dir对应的文件不存在，或者不是一个目录，则退出
        if (!dirFile.exists() || !dirFile.isDirectory())
        {
            return false;
        }
        boolean flag = true;
        //删除文件夹下的所有文件(包括子目录)
        File[] files = dirFile.listFiles();
        for (int i = 0; i < files.length; i++)
        {
            //删除子文件
            if (files[i].isFile())
            {
                flag = deleteFile(files[i].getAbsolutePath());
                if (!flag)
                    break;
            } //删除子目录
            else
            {
                flag = deleteDirectory(files[i].getAbsolutePath());
                if (!flag)
                    break;
            }
        }
        if (!flag)
            return false;
        //删除当前目录
        if (dirFile.delete())
        {
            return true;
        }
        else
        {
            return false;
        }
    }

    private static boolean exeCommond(String cmd)
    {
        boolean result = true;
        Process child = null;
        BufferedReader br = null;
        try
        {
            child = Runtime.getRuntime().exec(cmd);
            //防止waitFor()方法阻塞无法返回
            br = new BufferedReader(new InputStreamReader(child.getErrorStream(),"UTF-8"));
            String read;
            while ((read = br.readLine()) != null)
            {
                //System.out.println(read);
            }
            child.waitFor();
        }
        catch (Exception e)
        {
            logger.error("音视频转码错误" );
            result = false;
        }
        finally
        {
            child.destroy();
            try
            {
                br.close();
            }
            catch (IOException e)
            {
                e.printStackTrace();
            }
        }
        return result;
    }
    public static boolean mediaCodec(String ffmpegPath, String mediaPath, String codcFilePath, String type)
    {
        if (StringUtils.isEmpty(type) || !"audio,video".contains(type))
        {
            logger.error("传入的待转码的文件类型type不正确, 仅支持audio或video, type =" + type);
            return false;
        }
        StringBuilder cmd = new StringBuilder();
        cmd.append(" cmd /c ");
        cmd.append(ffmpegPath);

        if ("video".equals(type))
        {
            cmd.append(" -threads ");//工作线程数
            cmd.append(" 2 ");
            cmd.append(" -i "); // 添加参数＂-i＂，该参数指定要转换的文件
            cmd.append(mediaPath); // 添加要转换格式的视频文件的路径
            cmd.append(" -ab "); //设置音频码率（音频数据流量，一般选择32、64、96、128）
            cmd.append(" 64 ");
            cmd.append(" -ar "); //设置声音的采样频率
            cmd.append(" 22050 ");
            cmd.append(" -vcodec ");//使用libx264编码压缩视频
            cmd.append(" libx264 ");
            cmd.append(" -qscale "); //指定转换的质量, 4的质量比6高
            cmd.append(" 6 ");
            cmd.append(" -r "); //设置帧频
            cmd.append(" 24 ");
            cmd.append(" -ac "); //设置声道数
            cmd.append(" 2 ");
        }
        else if ("audio".equals(type))
        {
            cmd.append(" -i ");
            cmd.append(mediaPath);
            cmd.append(" -acodec ");//编码
            cmd.append(" libmp3lame ");//libmp3lame 或 mp3
        }

        cmd.append(" -y "); // 添加参数＂-y＂，该参数指定将覆盖已存在的文件
        cmd.append(codcFilePath);

        boolean result = exeCommond(cmd.toString());
        return result;
    }


    /**
     * ios版微信silk文件解码
     * @param silkpath silk文件路径
     * @param finalpath 最终输出的文件
     * @param type 仅能是silk
     */
    public static boolean mediaSilkCodecLinux(String silkpath, String finalpath, String type)
    {
        if (StringUtils.isEmpty(type) || !"silk".equalsIgnoreCase(type))
        {
            logger.error("传入的待转码的文件类型type不正确, 仅支持silk, type=" + type);
            return false;
        }
        String pcmpath = finalpath.substring(0, finalpath.lastIndexOf("."));
        pcmpath += ".pcm";
        //1. 先将silk文件转成pcm
        StringBuilder cmd = new StringBuilder();

        //cmd.append(" ./decode ");
        cmd.append(" /root/derrick/decoder ");
        cmd.append(silkpath);
        cmd.append( " " );
        cmd.append(pcmpath);
        logger.error(cmd.toString());
        if (!exeCommondLinux(cmd.toString())) {
            return false;
        }
        //2. 将pcm转成mp4
        if (mediaCodecLinux( pcmpath, finalpath, "audio","silk"))
            return true;
        else
            return false;
    }
    public static boolean mediaCodecLinux(String mediaPath, String codcFilePath, String type,String audiotype)
    {
        if (StringUtils.isEmpty(type) || !"audio,video".contains(type))
        {
            logger.error("传入的待转码的文件类型type不正确, 仅支持audio或video, type =" + type);
            return false;
        }
        StringBuilder cmd = new StringBuilder();
        cmd.append(" docker run -v ");
        cmd.append(" /:/ffmpeg ");
        cmd.append(" jrottenberg/ffmpeg ");
        if("audio".equals(type)) {
            if(null!=audiotype && audiotype.equals("silk"))
                cmd.append(" -y -f s16le -ar 24000 -ac 1 -i ");
            else
                cmd.append(" -y -i ");
        }
        if("video".equals(type))
            cmd.append(" -y -i ");
        cmd.append("/ffmpeg"+mediaPath);
        cmd.append(" ");
        cmd.append("/ffmpeg"+codcFilePath);
        logger.error(cmd.toString());
        boolean result = exeCommondLinux(cmd.toString());
        return result;
    }
    private static boolean exeCommondLinux(String cmd) {
        boolean result = true;
        try {
            String[] cmdA = { "/bin/sh", "-c", cmd };
            Process process = Runtime.getRuntime().exec(cmdA);
            LineNumberReader br = new LineNumberReader(new InputStreamReader(
                    process.getInputStream(),"UTF-8"));
            StringBuffer sb = new StringBuffer();
            String line;
            while ((line = br.readLine()) != null) {
                System.out.println(line);
                sb.append(line).append("\n");
            }
            process.waitFor();
            return true;
        } catch (Exception e) {
            logger.error("异常",e);
            e.printStackTrace();
        }
        return false;
    }
//    private static final String SILK_CODEC_PATH = PipConfig.getString("CODEC_INS_PATH") + "/SilkDecoder.exe";
}
